import { useCallback, useMemo, useState, useRef, useEffect } from 'react';
import './index.scss';
import DropdownList from './DropdownList';
import DownIcon from './DownIcon';
import { array, func, string, bool, shape, number, object } from 'prop-types';
import { addEvent, removeEvent } from '@/utils';

const DEFAULT_FIELD_NAMES = {
  label: 'label',
  value: 'value',
  children: 'children',
  disabled: 'disabled',
};

function Cascader({
  className,
  changeOnSelect,
  disabled,
  defalutValue,
  dropdownClassName,
  fieldNames,
  hasSearch,
  notFoundContent,
  options,
  onChange,
  onDropdownToggle,
  placeholder,
  splitItem,
  style,
}) {
  const field = useMemo(() => ({ ...DEFAULT_FIELD_NAMES, ...fieldNames }), [fieldNames]);
  const [value, setValue] = useState([]);
  const [searchValue, setSearchValue] = useState('');

  const autoSelect = useCallback(
    (children, target) => {
      if (!children?.length) return;
      const validChildren = children.filter(item => !item[field.disabled]);
      target.push(validChildren[0]);
      autoSelect(validChildren[0]?.[field.children] || [], target);
    },
    [field]
  );

  const getCompleteSelect = useCallback(
    value => {
      let res = [];
      let children = options;
      value.forEach(item => {
        const child = children.find(child => child[[field.value]] === item);
        if (child) {
          res.push(child || { [field.value]: item, [field.label]: item });
        }
        if (child?.children) {
          children = child[field.children] || [];
        }
      });
      setValue(res);
      return res;
    },
    [options, field]
  );

  const flatOptions = useMemo(() => {
    const res = [];
    let labelArr = [];
    let valueArr = [];
    function treeToList(children) {
      for (let i = 0; i < children.length; i++) {
        const item = children[i];
        if (item[field.disabled]) {
          continue;
        }
        if (item[field.children]?.length) {
          labelArr.push(item[field.label] || item[field.value]);
          valueArr.push(item[field.value]);
          treeToList(item[field.children]);
        } else {
          res.push({
            label: [...labelArr, item[field.label] || item[field.value]].join(splitItem),
            value: [...valueArr, item[field.value]],
          });
        }
        if (i === children.length - 1) {
          labelArr.pop();
          valueArr.pop();
        }
      }
    }
    hasSearch && treeToList(options);
    return res;
  }, [options, splitItem, field, hasSearch]);

  const searchNodes = useMemo(() => {
    const filterArr = flatOptions.filter(item => item.label.includes(searchValue));
    return filterArr.map(item => {
      const charIndex = item.label.indexOf(searchValue);
      const left = item.label.slice(0, charIndex);
      const right = item.label.slice(charIndex + searchValue.length);
      return (
        <div
          className="h-cascader-list-item"
          key={item.value.join()}
          onClick={() => {
            setValue(item[field.value]);
            const _selectNode = getCompleteSelect(item[field.value]);
            setSelectedNode(_selectNode);
          }}
        >
          <span className="h-cascader-list-item__label">
            {left}
            <span className="is-active">{searchValue}</span>
            {right}
          </span>
        </div>
      );
    });
  }, [flatOptions, searchValue, getCompleteSelect, field.value]);

  useEffect(() => {
    // 默认值转selectNode
    let _selectNode = [];
    if (defalutValue.length) {
      _selectNode = getCompleteSelect(defalutValue);
    } else if (options.length) {
      autoSelect(options, _selectNode);
    }
    setSelectedNode(_selectNode);
  }, []);

  const [selectedNode, setSelectedNode] = useState([]);
  const showLabel = useMemo(
    () => selectedNode.map(item => item?.[field.label]).join(` ${splitItem} `),
    [selectedNode, splitItem, field]
  );

  const cascaderRef = useRef();

  const [toggle, setToggle] = useState(false);
  function handleToggle(event) {
    if (disabled) return;
    setToggle(event);
    onDropdownToggle && onDropdownToggle(!toggle);
  }

  useEffect(() => {
    addEvent(document, 'click', clickEvent);
    function clickEvent(e) {
      if (!cascaderRef.current.contains(e.target)) {
        handleToggle(false);
      }
    }

    return () => {
      removeEvent(document, 'click', clickEvent);
    };
  });

  const listGroup = [];
  for (let i = 0; i <= selectedNode.length; i++) {
    const item = selectedNode[i];
    const list = i ? selectedNode[i - 1]?.[field.children] || [] : options;
    listGroup.push(
      <DropdownList
        key={i}
        field={field}
        selectedValue={item?.[field.value]}
        list={list}
        handleSelect={listItem => handleSelect(i, listItem)}
      ></DropdownList>
    );
    if (!item?.[field.children]) break;
  }

  function handleSelect(level, listItem) {
    const _selectedNode = [...selectedNode].slice(0, level);
    if (listItem) {
      _selectedNode.push(listItem);
      if (listItem.children) {
        autoSelect(listItem.children, _selectedNode);
      }
    }
    const newValue = _selectedNode.map(item => item?.[field.value]);
    setValue(newValue);
    setSelectedNode(_selectedNode);
    if (changeOnSelect) {
      onChange(newValue, _selectedNode);
    }
  }

  const handleClear = useCallback(() => {
    setSelectedNode([]);
    setValue([]);
    onChange([]);
  }, [onChange]);

  const handleConfirm = () => {
    const endItem = selectedNode[selectedNode.length - 1];
    if (selectedNode.length && (!endItem[field.children] || changeOnSelect)) {
      const newValue = selectedNode.map(item => item?.[field.value]);
      handleToggle(false);
      setSearchValue('');
      setValue(newValue);
      onChange && onChange(newValue, selectedNode);
    }
  };

  return (
    <div
      className={`h-cascader ${className} ${disabled ? 'h-disabled' : ''}`}
      ref={cascaderRef}
      style={style}
    >
      <div className="h-cascader-input" onClick={() => handleToggle(!toggle)}>
        <div className={`h-cascader-input__inner ${value.length ? '' : 'placeholder'}`}>
          {value.length ? showLabel : placeholder}
        </div>
        <DownIcon toggle={toggle}></DownIcon>
      </div>
      <div
        className={`h-cascader-dropdown ${className} ${dropdownClassName} ${
          toggle ? '' : 'is-closed'
        }`}
      >
        <input
          className="h-cascader-dropdown__search"
          placeholder="搜索"
          style={{ display: hasSearch ? 'block' : 'none' }}
          type="text"
          value={searchValue}
          onChange={e => setSearchValue(e.target.value)}
        />
        {options?.length ? (
          [
            searchValue ? (
              searchNodes.length ? (
                <div className="h-cascader-list search-list">{searchNodes}</div>
              ) : (
                <div className="no-data">未搜索到数据</div>
              )
            ) : (
              <div key="group" className="h-cascader-dropdown__group">
                {listGroup}
              </div>
            ),
            <div key="footer" className="h-cascader-dropdown__footer">
              <button className="h-cascader-button button-clear" onClick={() => handleClear()}>
                清空
              </button>
              <button className="h-cascader-button button-confirm" onClick={() => handleConfirm()}>
                确定
              </button>
            </div>,
          ]
        ) : (
          <div className="no-data">{notFoundContent}</div>
        )}
      </div>
    </div>
  );
}

Cascader.prototype = {
  className: string,
  changeOnSelect: bool,
  defalutValue: array,
  disabled: bool,
  displayRender: func,
  dropdownClassName: string,
  fieldNames: shape({
    label: string,
    value: string || number,
    children: string,
  }),
  notFoundContent: string,
  options: array,
  onChange: func,
  placeholder: string,
  splitItem: string,
  style: object,
};

Cascader.defaultProps = {
  className: '',
  changeOnSelect: false,
  defalutValue: [],
  disabled: false,
  dropdownClassName: '',
  fieldNames: {},
  notFoundContent: '暂无数据',
  options: [],
  onChange: () => {},
  placeholder: '请选择',
  splitItem: '-',
  style: {},
};
export default Cascader;
